/* @flow */

import {
    warn,
    remove,
    isObject,
    parsePath,
    _Set as Set,
    handleError,
    invokeWithErrorHandling,
    noop
} from '../util/index';

import { traverse } from './traverse';
import { queueWatcher } from './scheduler';
import Dep, { pushTarget, popTarget } from './dep';

import type { SimpleSet } from '../util/index';

let uid = 0;

/**
 * A watcher parses an expression, collects dependencies,
 * and fires callback when the expression value changes.
 * This is used for both the $watch() api and directives.
 *
 * Watcher扮演的角色是观察者，它关心数据，在数据变化后能够获得通知，并作出处理。
 * 一个组件里可以有多个Watcher类实例，Watcher类包装观察者函数，而观察者函数使用数据。
 * 观察者函数经过Watcher是这么被包装的： - 模板渲染：this._watcher = new Watcher(this, render, this._update) - 计算属性
 *
 * watcher 解析表达式、收集依赖
 * 当表达式值改变时触发回调
 * 它同时用于$watch() api 和 directives
 */
export default class Watcher {
    vm: Component;
    expression: string;
    cb: Function;
    id: number;
    deep: boolean;
    user: boolean;
    lazy: boolean;
    sync: boolean;
    dirty: boolean;
    active: boolean;
    deps: Array<Dep>;
    newDeps: Array<Dep>;
    depIds: SimpleSet;
    newDepIds: SimpleSet;
    before: ?Function;
    getter: Function;
    value: any;

    // 组件实例、观察者函数、回调函数、选项
    constructor(
        vm: Component,
        expOrFn: string | Function,
        cb: Function,
        options?: ?Object,
        isRenderWatcher?: boolean //是否为渲染watcher
    ) {
        this.vm = vm;
        if (isRenderWatcher) {
            vm._watcher = this;
        }
        // 所有的watcher放入 vm._watchers中
        vm._watchers.push(this);
        // options
        // 和渲染watcher无关，非渲染watcher可能会传入选项。渲染watcher默认都是false。
        // before 调用时传入的before钩子函数，beforeUpdate
        if (options) {
            this.deep = !!options.deep;
            this.user = !!options.user;
            this.lazy = !!options.lazy;
            this.sync = !!options.sync;
            this.before = options.before;
        } else {
            // 如果没有传入选项，全部为false
            this.deep = this.user = this.lazy = this.sync = false;
        }
        //为用户传入的watcher时，此处为用户传入的回调，对比前后两个值
        this.cb = cb;

        // 唯一标识watcher，uid自增。
        this.id = ++uid; // uid for batching

        // 标识当前watcher是否为活动watcher，默认为true
        this.active = true;
        this.dirty = this.lazy; // for lazy watchers
        this.deps = []; //缓存上一轮执行观察者函数用到的dep实例
        this.newDeps = []; //存储本轮执行观察者函数用到的dep实例
        this.depIds = new Set(); //Hash表，用于快速查找
        this.newDepIds = new Set(); //Hash表，用于快速查找
        this.expression = process.env.NODE_ENV !== 'production' ? expOrFn.toString() : '';
        // parse expression for getter
        if (typeof expOrFn === 'function') {
            // 如果为函数存入 getter
            this.getter = expOrFn;
        } else {
            // 字符串。侦听器时，传入字符串，
            // expOrFn 是字符串的时候，例如 watch: { 'person.name': function... }
            // parsePath('person.name') 返回一个函数获取 person.name 的值。parsePath生成一个函数获取 person.name 的值
            this.getter = parsePath(expOrFn); //此时getter是一个函数，函数返回当前watcher的值。
            if (!this.getter) {
                this.getter = noop;
                process.env.NODE_ENV !== 'production' &&
                    warn(
                        `Failed watching path: "${expOrFn}" ` +
                            'Watcher only accepts simple dot-delimited paths. ' +
                            'For full control, use a function instead.',
                        vm
                    );
            }
        }
        // 计算属性，lazy为true，延时执行
        this.value = this.lazy ? undefined : this.get();
    }

    /**
     * Evaluate the getter, and re-collect dependencies.
     */
    get() {
        // 把当前的watcher对象入栈。把当前的watcher保存到
        pushTarget(this);
        let value;
        const vm = this.vm;
        try {
            // getter为传入的updateComponent
            // getter为函数，如果是渲染watcher，则为updateComponent
            value = this.getter.call(vm, vm);
        } catch (e) {
            if (this.user) {
                handleError(e, vm, `getter for watcher "${this.expression}"`);
            } else {
                throw e;
            }
        } finally {
            // "touch" every property so they are all tracked as
            // dependencies for deep watching
            // 为深度监听做准备，监听对象的子属性
            if (this.deep) {
                traverse(value);
            }
            popTarget();
            // 将watcher从dep的subs中移除。同时将watcher中的dep移除
            this.cleanupDeps();
        }
        return value;
    }

    /**
     * Add a dependency to this directive.
     * 向这个指令添加一个依赖项
     */
    addDep(dep: Dep) {
        // 每个dep都有一个id属性，每创建一个dep，对id ++
        const id = dep.id;
        // newDepIds Set类型值，唯一不重复的对象
        if (!this.newDepIds.has(id)) {
            // 将dep对象和对应id添加到watcher对象里
            this.newDepIds.add(id);
            this.newDeps.push(dep);
            if (!this.depIds.has(id)) {
                // 将watcher添加到subs数组中
                dep.addSub(this);
            }
        }
    }

    /**
     * Clean up for dependency collection.
     */
    cleanupDeps() {
        let i = this.deps.length;
        while (i--) {
            const dep = this.deps[i];
            if (!this.newDepIds.has(dep.id)) {
                dep.removeSub(this);
            }
        }
        let tmp = this.depIds;
        this.depIds = this.newDepIds;
        this.newDepIds = tmp;
        this.newDepIds.clear();
        tmp = this.deps;
        this.deps = this.newDeps;
        this.newDeps = tmp;
        this.newDeps.length = 0;
    }

    /**
     * Subscriber interface.
     * Will be called when a dependency changes.
     */
    update() {
        /* istanbul ignore else */
        if (this.lazy) {
            this.dirty = true;
        } else if (this.sync) {
            this.run();
        } else {
            queueWatcher(this);
        }
    }

    /**
     * Scheduler job interface.
     * Will be called by the scheduler.
     */
    run() {
        // active 标记是否为活动状态，默认为true，可被处理
        if (this.active) {
            // 调用watcher get方法
            // 渲染 watcher get()无返回值，value为undefined
            const value = this.get();
            if (
                value !== this.value ||
                // Deep watchers and watchers on Object/Arrays should fire even
                // when the value is the same, because the value may
                // have mutated.
                isObject(value) ||
                this.deep
            ) {
                // set new value
                const oldValue = this.value;
                this.value = value;
                // this.user 是否为用户watcher
                if (this.user) {
                    const info = `callback for watcher "${this.expression}"`;
                    invokeWithErrorHandling(this.cb, this.vm, [value, oldValue], this.vm, info);
                } else {
                    this.cb.call(this.vm, value, oldValue);
                }
            }
        }
    }

    /**
     * Evaluate the value of the watcher.
     * This only gets called for lazy watchers.
     */
    evaluate() {
        this.value = this.get();
        this.dirty = false;
    }

    /**
     * Depend on all deps collected by this watcher.
     */
    depend() {
        let i = this.deps.length;
        while (i--) {
            this.deps[i].depend();
        }
    }

    /**
     * Remove self from all dependencies' subscriber list.
     */
    teardown() {
        if (this.active) {
            // remove self from vm's watcher list
            // this is a somewhat expensive operation so we skip it
            // if the vm is being destroyed.
            if (!this.vm._isBeingDestroyed) {
                remove(this.vm._watchers, this);
            }
            let i = this.deps.length;
            while (i--) {
                this.deps[i].removeSub(this);
            }
            this.active = false;
        }
    }
}
